#include "objloader.h"
#include "../../core/point.h"
#include "../../core/normal.h"
#include "../../core/vector3.h"
#include "../../core/geometry.h"

// slightly modified from wavefront.cpp by Mark Colbert
// parser for loading obj file, only support triangle

#define LINE_BUFFER_SIZE 1024

// GETNUM just gets the next number from a line of input in an OBJ file
#ifndef GETNUM
#define GETNUM(lineBuffer, numBuffer, lindex, nindex, tval)  \
	nindex=0;\
	while ((lineBuffer[lindex] == ' ') || lineBuffer[lindex] == '/') lindex++;\
	while ((lineBuffer[lindex] != ' ') && (lineBuffer[lindex] != '/') && \
	(lineBuffer[lindex] != '\0') && (lineBuffer[lindex] != '\n') && (lindex != LINE_BUFFER_SIZE)) { \
	numBuffer[nindex] = lineBuffer[lindex]; \
	nindex++; \
	lindex++; \
	} \
	numBuffer[nindex] = '\0'; \
	tval = atoi(numBuffer);
#endif


bool LoadObj(const string filename, int *nt, int *nv, int *&vi, Point3 *&P, Normal *&N, Point2 *&uv) {
	FILE *fp;
	fp = fopen(filename.c_str(),"r");
	if(!fp) {
		Warning("Unable to open file %s to load\n", filename.c_str());
		return false;
	}
	// Temporary input buffers
	
	// Index of triangle
	vector<int> vertexIndex;
	vector<int> normalIndex;
	vector<int> uvIndex;

	// Real data
	vector<Point3> points;
	vector<Normal> normals;
	vector<Point2> uvs;

	// read input
	Point3 ptmp;
	Normal ntmp;
	Point2 uvtmp;

	char lineBuffer[LINE_BUFFER_SIZE];
	char numBuffer[32];
	int lindex = 0;
	int nindex = 0;
	int ival, uvval, nval;
	
	*nt = 0;

	// parse the data in
	while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp)) {
		switch (lineBuffer[0]) {
			case 'v':
				// vertex information
				if(lineBuffer[1] == ' ') {
					// regular vertex point
					sscanf(&lineBuffer[2], "%f %f %f", &ptmp.x, &ptmp.y, &ptmp.z);
					points.push_back(ptmp);
				}
				else if(lineBuffer[1] == 't') {
					// texture coordinates
					sscanf(&lineBuffer[3], "%f %f", &uvtmp.x, &uvtmp.y);
					uvs.push_back(uvtmp);
				}
				else if(lineBuffer[1] == 'n') {
					// normal
					sscanf(&lineBuffer[3], "%f %f %f", &ntmp.x, &ntmp.y, &ntmp.z);
					normals.push_back(ntmp);
				}
				break;
			case 'f':
				// face information
				// four possible layout
				// case 1: f v1			v2			v3
				// case 2: f v1/vt1		v2/vt2		v3/vt3    <--|
				// case 3: f v1//vn1	v2//vn2		v3//vn3   <--|	be careful with these two!
				// case 4: f v1/vt1/vn1	v2/vt2/vn2	v3/vt3/vn3

				lindex = 2;
				// Add one face
				(*nt)++;
				for(int i = 0; i < 3; i++) {

					GETNUM(lineBuffer, numBuffer, lindex, nindex, ival)
					
					// Obj files go from 1..n, drop 1 to start from 0 to n-1
					ival--;
					vertexIndex.push_back(ival);

					if(lineBuffer[lindex] == '/' && lineBuffer[lindex+1] != '/') {
						// texture coordinate index
						lindex++;
						GETNUM(lineBuffer, numBuffer, lindex, nindex, uvval)
						uvIndex.push_back(uvval-1);
					}

					if(lineBuffer[lindex] == '/') {
						// normal index
						lindex++;
						GETNUM(lineBuffer,numBuffer, lindex, nindex, nval)
						normalIndex.push_back(nval-1);
					}
					lindex++;
				}
				break;
			case 'g':
			default:
				// not really caring about faces or materials now
				// so just making life easier, I'll ignoring it
				break;
		}
	}

	fclose(fp);

	// Merge indicies to one single index
	
	bool useNormals = !normals.empty();
	bool useUVs = !uvs.empty();

	if(!useNormals && !useUVs) {
	#ifndef NDEBUG
		std::cout << "Copying points" << std::endl;
	#endif
		// just copy the points into array
		*nv = points.size();
		P = new Point3[*nv];
		vi = new int[vertexIndex.size()];
		for(int i = 0; i < *nv; i++)
			P[i] = points[i];
		for(int i = 0; i < vertexIndex.size(); i++)
			vi[i] = vertexIndex[i];
		N = NULL;
		uv = NULL;
	#ifndef NDEBUG
		std::cout << "Obj Loaded with "<< *nv <<" vertex and " << *nt << " faces" << std::endl;
		std::cout << "Used no normals" << std::endl;
		std::cout << "Used no texture coords" << std::endl;
	#endif
		return true;
	}

	// assumes that vertexIndex = normalIndex = uvIndex
	// We create as many vertex as index to make sure that every vertex has proper normal&uv

	*nv = 3 * *nt;
	vi = new int[*nv];
	P = new Point3[*nv];
	if(useNormals) 
		N = new Normal[*nv];
	else 
		N = NULL;
	if(useUVs) 
		uv = new Point2[*nv];
	else
		uv = NULL;

	for(int i = 0; i < *nv; i++) {
		P[i] = points[vertexIndex[i]];
		if(useNormals)
			N[i] = normals[normalIndex[i]];
		if(useUVs)
			uv[i] = uvs[uvIndex[i]];
		vi[i] = i;
	}

	points.clear();
	normals.clear();
	uvs.clear();
	vertexIndex.clear();
	normalIndex.clear();
	uvIndex.clear();

#ifndef NDEBUG
	std::cout << "Obj Loaded with "<< *nv <<" vertex and " << *nt << " faces" << std::endl;
	if (useNormals) std::cout << "Used normals" << std::endl;
	if (useUVs) std::cout << "Used texture coords" << std::endl;
#endif

	return true;
}